package com.jml.每特.mybatis注解.aop;

import com.jml.每特.mybatis注解.annotation.ExtInsert;
import com.jml.每特.mybatis注解.annotation.ExtParam;
import com.jml.每特.mybatis注解.annotation.ExtSelect;
import com.jml.每特.mybatis注解.utils.JDBCUtils;
import com.jml.每特.mybatis注解.utils.SQLUtils;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

//使用jdk动态代理拦截接口
public class MyInvocationHandlerMybatis implements InvocationHandler {

	private Object object;

	public MyInvocationHandlerMybatis(Object object) {
		this.object = object;
	}

	/*proxy是代理对象，method是拦截的方法，args是方法上的参数*/
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("使用动态代理技术拦截接口方法，开始......");
		//1.判断方法上是否有@ExtInsert注解
		ExtInsert extInsert = method.getDeclaredAnnotation(ExtInsert.class);
		if(extInsert !=null){
			return insertAnnotation(extInsert,method,args);
		}
		ExtSelect extSelect = method.getDeclaredAnnotation(ExtSelect.class);
		//如果是查询语句有注解ExtSelect
		if(extSelect!=null){
			return selectAnnotation(extSelect, method, args);
		}
		return null;
	}

	private Object selectAnnotation(ExtSelect extSelect,Method method, Object[] args)
			throws Exception {
		String insertSql = extSelect.value();
		System.out.println("执行的sql语句为："+insertSql);
		//3.获取方法的参数和sql参数进行匹配
		//获取方法上的参数 key是注解中值，value是参数的实际值
		ConcurrentHashMap<String, Object> paramMap = getParamMap(method,args);
		//存放sql执行的参数
		List<Object> sqlParams = new ArrayList<Object>();
		//获取sql中的#{}里面的字符
		List<String> sqlSelectParameter = SQLUtils.sqlSelectParameter(insertSql);
		//参数进行匹配
		for (int i = 0; i < sqlSelectParameter.size(); i++) {
			Object o = paramMap.get(sqlSelectParameter.get(i));
			sqlParams.add(o);
		}
		//将#{}替换为？
		insertSql = SQLUtils.parameQuestion(insertSql, sqlSelectParameter);
		System.out.println("newsql:"+insertSql);
		ResultSet query = JDBCUtils.query(insertSql, sqlParams);
		//判断是否有结果集，使用反射实例化对象，获取方法返回类型，进行实例化。
		if(!query.next()){
			return null;
		}
		return getResultByReflect(query,method);
	}

	private Object getResultByReflect(ResultSet query,Method method) throws Exception {
		//下标往上移动移位
		query.previous();
		//使用反射获取类型
		Class<?> returnType = method.getReturnType();
		//如果是list类型，就不可以通过newInstance生成实例，我们需要手动new ArrayList(),因为接口不可以实例化
		Object object = returnType.newInstance();
		while (query.next()){
			Field[] fields = returnType.getDeclaredFields();
			for (Field field: fields) {
				field.setAccessible(true);
				Object objectField = query.getObject(field.getName());
				field.set(object,objectField);
			}
			/*for (String name: sqlSelectParameter) {
				Object resultValue = query.getObject(name);
				//使用反射机制赋值
				Field field = returnType.getDeclaredField(name);
				field.setAccessible(true);
				field.set(object,resultValue);
			}*/
		}
		return object;
	}
	private Object insertAnnotation(ExtInsert extInsert,Method method, Object[] args){
		//2.获取sql语句，获取注解@ExtInsert的语句
		String selectSql = extInsert.value();
		System.out.println("执行的sql语句为："+selectSql);
		//3.获取方法的参数和sql参数进行匹配
		//获取方法上的参数 key是注解中值，value是参数的实际值
		ConcurrentHashMap<String, Object> paramMap = getParamMap(method,args);
		//存放sql执行的参数
		List<Object> sqlParams = new ArrayList<Object>();
		//获取sql中的#{}里面的字符
		String[] strings = SQLUtils.sqlInsertParameter(selectSql);
		for (int i = 0; i < strings.length; i++) {
			Object o = paramMap.get(strings[i]);
			sqlParams.add(o);
		}
		//将#{}替换为？
		selectSql = SQLUtils.parameQuestion(selectSql, strings);
		//执行sql
		int insert = JDBCUtils.insert(selectSql, false, sqlParams);
		return insert;
	}

	private ConcurrentHashMap<String, Object> getParamMap(Method method,Object[] args){
		ConcurrentHashMap<String, Object> paramMap = new ConcurrentHashMap<>();
		Parameter[] parameters = method.getParameters();
		for (int i = 0; i < parameters.length; i++) {
			Parameter parameter = parameters[i];
			ExtParam extParam = parameter.getDeclaredAnnotation(ExtParam.class);
			if(extParam!=null){
				//@ExtParam注解中值
				String paramName = extParam.value();
				//参数的值
				Object value = args[i];
				System.out.println("注解中值paramName:"+paramName+"---"+"传递的值value:"+value);
				paramMap.put(paramName,value);
			}
		}
		return paramMap;
	}
}
